package com.gzsxy.esjy.service.auth.controller.api;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.gzsxy.esjy.bus.service.LoginlogService;
import com.gzsxy.esjy.common.base.result.ResultCodeEnum;
import com.gzsxy.esjy.common.base.util.*;
import com.gzsxy.esjy.redis.extend.RedisService;
import com.gzsxy.esjy.service.auth.entity.User;
import com.gzsxy.esjy.service.auth.filter.DefaultUser;
import com.gzsxy.esjy.service.auth.properties.UcenterProperties;
import com.gzsxy.esjy.service.auth.service.UserService;
import com.gzsxy.esjy.service.base.DictionaryConstants;
import com.gzsxy.esjy.service.base.dto.request.LoginLogRequest;
import com.gzsxy.esjy.service.base.dto.response.UserRsp;
import com.gzsxy.esjy.service.base.handler.GuliEception;
import com.vdurmont.emoji.EmojiParser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.server.ServerWebExchange;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author xiaolong
 * @version 1.0
 * @description: 微信认证
 * @date 2022/3/5 11:33
 */
@CrossOrigin  //解决跨域
@RefreshScope    //动态刷新配置文件-外部配置文件
@Controller //注意这里没有配置 @RestController
@RequestMapping("/api/educenter/wx")
@Api("微信认证")
@Slf4j
public class ApiWxController {


    @Autowired
    private UcenterProperties ucenterProperties;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private LoginlogService loginlogService;

    @GetMapping("login")
    @ApiOperation("微信登录")
    public String genQrConnect(HttpSession session){

        //组装url：https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=回调地址&response_type=code&scope=snsapi_login&state=随机数#wechat_redirect
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        //将回调url进行转码
        String redirectUri = "";
        try {
            redirectUri = URLEncoder.encode(ucenterProperties.getRedirectUri(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(ExceptionUtils.getMessage(e));
            throw new GuliEception(ResultCodeEnum.URL_ENCODE_ERROR);
        }

        //生成随机state，防止csrf攻击
        String state = UUID.randomUUID().toString();
        //将state存入session
//        session.setAttribute("wx_open_state", state);
        //正常不这样做，正常是通过session解决，不然多用户登录无法确认唯一性
        redisService.setValue("wx_open_state",state,10);
        log.info("登录时【wx_open_state】：{}",state);

        String qrcodeUrl = String.format(
                baseUrl,
                ucenterProperties.getAppId(),
                redirectUri,
                state);
        //跳转到组装的url地址中去
        return "redirect:" + qrcodeUrl;
    }


    /**
     * @description: 回调地址
     * @param
     * @return
     */
    @GetMapping("callback")
    @ApiOperation("微信登录回调")
    public String callback(String code, String state, HttpSession session, HttpServletRequest request){
        System.out.println("callback被调用");
        System.out.println("code:" + code);
        System.out.println("state:" + state);

        if(StringUtils.isEmpty(code) || StringUtils.isEmpty(state)){
            log.error("非法回调请求");
            throw new GuliEception(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

//        String sessionState = (String)session.getAttribute("wx_open_state");
        String wx_open_state = (String)redisService.getValue("wx_open_state");
        System.out.println("xxxxxxx"+wx_open_state);
        if(!state.equals(wx_open_state)){
            log.error("非法回调请求");
            throw new GuliEception(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }

        //携带code临时票据，和appid以及appsecret请求access_token和openid（微信唯一标识）
        String accessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
        //组装参数：?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        Map<String, String> accessTokenParam = new HashMap<>();
        accessTokenParam.put("appid", ucenterProperties.getAppId());
        accessTokenParam.put("secret", ucenterProperties.getAppSecret());
        accessTokenParam.put("code", code);
        accessTokenParam.put("grant_type", "authorization_code");
        HttpClientUtils client = new HttpClientUtils(accessTokenUrl, accessTokenParam);

        String result = "";
        try {
            //发送请求：组装完整的url字符串、发送请求
            client.get();
            //得到响应
            result = client.getContent();
            System.out.println("result = " + result);
        } catch (Exception e) {
            log.error("获取access_token失败");
            throw new GuliEception(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        Gson gson = new Gson();
        HashMap<String, Object> resultMap = gson.fromJson(result, HashMap.class);
        //失败的响应结果
        Object errcodeObj = resultMap.get("errcode");
        if(errcodeObj != null){
            Double errcode = (Double)errcodeObj;
            String errmsg = (String)resultMap.get("errmsg");
            log.error("获取access_token失败：" + "code：" + errcode + ", message：" +  errmsg);
            throw new GuliEception(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }

        //解析出结果中的access_token和openid
        String accessToken = (String)resultMap.get("access_token");
        String openid = (String)resultMap.get("openid");

        //TODO 在本地数据库中查找当前微信用户的信息
        UserRsp user = userService.getByOpenid(openid);
        //如果当前用户不存在
        if(ObjectUtils.isEmpty(user)){
            //if：如果当前用户不存在，则去微信的资源服务器获取用户个人信息（携带access_token）
            String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo";
            //组装参数：?access_token=ACCESS_TOKEN&openid=OPENID
            Map<String, String> baseUserInfoParam = new HashMap<>();
            baseUserInfoParam.put("access_token", accessToken);
            baseUserInfoParam.put("openid", openid);
            client = new HttpClientUtils(baseUserInfoUrl, baseUserInfoParam);

            String resultUserInfo = "";
            try {
                client.get();
                resultUserInfo = client.getContent();
            } catch (Exception e) {
                log.error(ExceptionUtils.getMessage(e));
                throw new GuliEception(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }

            HashMap<String, Object> resultUserInfoMap = gson.fromJson(resultUserInfo, HashMap.class);
            //失败的响应结果
            errcodeObj = resultUserInfoMap.get("errcode");
            if(errcodeObj != null){
                Double errcode = (Double)errcodeObj;
                String errmsg = (String)resultMap.get("errmsg");
                log.error("获取用户信息失败：" + "code：" + errcode + ", message：" +  errmsg);
                throw new GuliEception(ResultCodeEnum.FETCH_USERINFO_ERROR);
            }

            //解析出结果中的用户个人信息
            String nickname = (String)resultUserInfoMap.get("nickname");
            String avatar = (String)resultUserInfoMap.get("headimgurl");
            Double sex = (Double)resultUserInfoMap.get("sex");

            //在本地数据库中插入当前微信用户的信息（使用微信账号在本地服务器注册新用户）
            user = new UserRsp();
            user.setOpenId(openid);
            user.setName(EmojiParser.removeAllEmojis(nickname));
            user.setImagePath(avatar);
            user.setSex(sex.intValue());
            user.setUserType(String.valueOf(DictionaryConstants.NUMBER_1));
            User user1 = new User();
            BeanUtils.copyProperties(user,user1);
            userService.save(user1);
        }

        //则直接使用当前用户的信息登录（生成jwt）
        //member =>Jwt
        JwtInfo jwtInfo = new JwtInfo();
        jwtInfo.setId(user.getId());
        jwtInfo.setName(user.getName());
        jwtInfo.setImagePath(user.getImagePath());
        String jwtToken = JwtUtils.getJwtToken(jwtInfo, 1800);
        user.setAccessToken(jwtToken);

        UserRsp finalUser = user;
        try {
            ThreadPoolUtil.getPool().execute(()->{
                this.addLoginlog(request, finalUser);
            });
        } catch (Exception e) {
            log.error("微信登录日志入库失败，{}",e);
        }
        return "redirect:http://8.134.34.190:4000?token=" + jwtToken;
    }



    /**
     * @description: 登录日志
     * @param
     * @return
     */
    public void addLoginlog(HttpServletRequest request, UserRsp user){
        LoginLogRequest logRequest = new LoginLogRequest();
        logRequest.setUserName(user.getUserName());
        logRequest.setIp(RequestUtil.getClientIp(request));
        logRequest.setLoginTime(new Date());
        String browserName = RequestHolder.getBrowserName(request);
        if (browserName.equals("Unknown")){
            logRequest.setBrowserName("Unknown");
            logRequest.setVersion("Unknown");
            logRequest.setOsName("Unknown");
        } else {
            logRequest.setBrowserName(RequestHolder.getBrowserName(request));
            logRequest.setOsName(RequestHolder.getOsName(request));
            logRequest.setVersion(RequestHolder.getBrowserVersion(request));
        }
        logRequest.setSchool(user.getSchool());
        logRequest.setResponseParam(JSON.toJSONString(user));
        loginlogService.addLoginlog(logRequest);
    }
}
